<?php

declare(strict_types=1);

namespace App\Model;

use App\Helpers\ConfigUtil;
use App\Helpers\PayUtil;
use App\Helpers\SandPayUtil;
use App\Model\Setting\TmpSetting;
use Hyperf\Database\Model\Builder;
use Hyperf\Utils\Context;
use Hyperf\DbConnection\Db;
use Hyperf\DbConnection\Model\Model;
use Hyperf\Cache\Annotation\Cacheable;
/**
 *  充值订单
 */
class PayOrder extends Model
{
    protected $table = 'pay_order';

    //待支付
    const STATUS_ING = 0;
    //支付成功
    const STATUS_OK = 1;
    //支付超时
    const STATUS_EXP = 2;
    //支付失败
    const STATUS_FAIL = 3;
    //标记其他
    const STATUS_EXTRA = 4;

    const STATUS = [
        self::STATUS_ING => '待支付',
        self::STATUS_OK => '支付成功',
        self::STATUS_EXP => '支付超时',
        self::STATUS_FAIL => '支付失败',
        self::STATUS_EXTRA => '未知状态'
    ];

    //微信支付
    const CHANNEL_WX = 'wechat';
    //支付宝
    const CHANNEL_ALI = 'alipay';
    //苹果内购
    const CHANNEL_APPLE = 'apple';
    //杉德微信
    const CHANNEL_SAND_WX = 'sd_wechat';
    //杉德支付宝
    const CHANNEL_SAND_ALI = 'sd_alipay';
    const CHANNEL_VALID = [
        self::CHANNEL_ALI,
        self::CHANNEL_WX,
        self::CHANNEL_APPLE,
        self::CHANNEL_SAND_ALI,
        self::CHANNEL_SAND_WX,
    ];

    //支付渠道-原生
    const PAY_FROM_NATIVE = 0;
    //支付渠道-杉德
    const PAY_FROM_SAND = 1;

    //充值入口
    //活动折扣
    const FROM_ACT_DISCOUNT = 7;
    const FROM = [
        0 => '充值页（默认）',
        2 => '私信拉起',
        3 => '通话拉起',
        4 => '礼物拉起',
        5 => '搭讪',
        6 => '查看微信',
        7 => '特惠活动',
    ];

    /**
     * 用户首次支付成功的记录
     */
    static function checkSuccess($userId = 0, $offDate = '')
    {
        $where = [
            ['user_id', $userId],
        ];
        $offDate && $where[] = ['finished_at', '>', (string)$offDate];
        return self::where($where)->whereIn('status', [self::STATUS_OK])->orderBy('id', 'desc')->first();
    }

    /**
     * 充值总额
     */
    static function getSuccessTotal($userId = 0, $offDate = '')
    {
        $where = [
            ['user_id', $userId],
        ];
        $offDate && $where[] = ['finished_at', '>', (string)$offDate];
        $data = self::where($where)->whereIn('status', [self::STATUS_OK])->select(Db::raw('sum(total_amount) as total'))->first();
        return $data && $data->total ? intval($data->total) : 0;
    }

    /**
     * 用户支付成功的总次数
     */
    static function checkSuccessCount($userId = 0, $offDate = '', $actPrice = false)
    {
        $where = [
            ['user_id', $userId],
            // ['status', self::STATUS_OK]
        ];
        $offDate && $where[] = ['finished_at', '>', (string)$offDate];
        $query = self::where($where)->whereIn('status', [self::STATUS_OK]);
        $actSubjectIds = [100, 101];
        return $actPrice ? $query->whereIn('subject_id', $actSubjectIds)->count() : $query->count();
    }

    /**
     * 用户支付成功的总金额
     */
    static function checkSuccessAmount($userId = 0, $startTime = '')
    {
        $where = [
            ['user_id', $userId],
            ['status', self::STATUS_OK]
        ];
        if ($startTime) $where[] = ['finished_at', '>', (string)$startTime];
        $data = self::where($where)->select(Db::raw('SUM(total_amount) as amount'))->get();
        return $data[0]->amount ?: 0;
    }

    /**
     * 更新成功状态
     */
    static function updateSuccess($orderId = 0)
    {
        if (!$orderId) return false;
        $status = self::STATUS_OK;
        return self::where('id', $orderId)->limit(1)->update([
            'status' => $status,
            'finished_at' => date('Y-m-d H:i:s'),
        ]);
    }

    /**
     * 获取微信商户号ID
     */
    static function getWxMchId($totalAmount = 0)
    {
        $appId = Context::get('app_id');

        return mt_rand(0, 9999) > 5000 ? env('WECHAT_MCH_ID', '') : env('WECHAT_MCH_ID2', '');
    }

    /**
     * 获取支付方式
     */
    static function getPayWay($payChannel = PayOrder::CHANNEL_WX, $newPay = true)
    {
        if (!$newPay) return PayOrder::PAY_FROM_NATIVE;
        if (PayOrder::CHANNEL_ALI == $payChannel) {
            $payFrom = env('PAY_FROM_ALI', PayOrder::PAY_FROM_NATIVE);
        } else {
            $payFrom = env('PAY_FROM_WX', PayOrder::PAY_FROM_NATIVE);
        }
        return $payFrom;
    }

    /**
     * 创建订单
     */
    static function createOrder(
        $userId = 0,
        $channel = 'wechat',
        $subjectId = 1,
        $clientIp = '',
        $from = 0
    ) {
        $subject = ConfigUtil::getRecharge($subjectId, $channel);
        if (!$subject) {
            log_user_result($userId, $subjectId . '==' . $channel);
            return false;
        }
        //总金额
        $totalAmount = $subject['rmb'] * 100;

        //微信商户号
        $wxMchId = $appKey = '';
        switch ($channel) {
            case self::CHANNEL_WX:
                $wxMchId = self::getWxMchId($totalAmount);
                break;
            case self::CHANNEL_ALI:
                $appKey = config('pay.alipay.app_key_prefix');
                $wxMchId = config('pay.alipay.app_id');
                break;
        }

        //订单记录
        $result = self::addRecord(
            $userId,
            $channel,
            $totalAmount,
            $subjectId,
            $subject['coin'],
            $subject['send'] ?? 0,
            $from,
            $wxMchId
        );
        if (empty($result)) return false;

        //生成支付链接
        $payInfo = [
            'total_amount' => $result['total_amount'],
            'subject' => $subject['subject_name'],
            'out_trade_no' => $result['out_trade_no'],
            'client_ip' => $clientIp,
        ];
        switch ($channel) {
                //微信支付
            case self::CHANNEL_WX:
                $payInfo['wx_mch_id'] = $wxMchId;
                $wxPayInfo = PayUtil::wechat($payInfo);
                //更新prepayId
                self::where('out_trade_no', $result['out_trade_no'])->limit(1)->update(['wx_prepay_id' => $wxPayInfo['prepayid'] ?? '']);
                return [
                    'wechatpay_info' => $wxPayInfo,
                    'order_id' => $result['order_id'],
                ];
                //支付宝
            case self::CHANNEL_ALI:
                $payInfo['app_key'] = $appKey;
                //微信和支付宝计量单位不一样
                $payInfo['total_amount'] = $subject['rmb'];
                return [
                    'alipay_info' => PayUtil::alipay($payInfo),
                    'order_id' => $result['order_id'],
                ];
                //杉德-微信
            case self::CHANNEL_SAND_WX:
                $sandPayInfo = SandPayUtil::orderCreate($payInfo, self::CHANNEL_WX);
                return [
                    'pay_info' => $sandPayInfo,
                    'order_id' => $result['order_id'],
                ];
                //杉德-H支付宝
            case self::CHANNEL_SAND_ALI:
                $sandPayInfo = SandPayUtil::orderCreate($payInfo, self::CHANNEL_ALI);
                return [
                    'pay_info' => $sandPayInfo,
                    'order_id' => $result['order_id'],
                ];
        }
        return ['order_id' => $result['order_id']];
    }

    /**
     * 添加订单记录
     */
    static function addRecord(
        $userId = 0,
        $channel = 'wechat',
        $totalAmount = 0,
        $subjectId = 0,
        $coin = 0,
        $sendCoin = 0,
        $from = 0,
        $wxMchId = ''
    ) {
        $model = new self();
        $model->user_id = $userId;
        $model->order_id = self::genOrderId($userId);
        $model->out_trade_no = self::genOutTradeNo($userId, $channel);
        $model->total_amount = $totalAmount;
        $model->channel = $channel;
        $model->subject_id = $subjectId;
        $model->status = self::STATUS_ING;
        $model->recharge_coin = $coin;
        $model->send_coin = $sendCoin;
        $model->from = $from;
        $model->wx_mch_id = $wxMchId;
        return $model->saveOrFail() ? $model->toArray() : [];
    }

    /**
     * 生成商家订单号
     */
    static function genOutTradeNo($userId = 0, $channel = 'wechat')
    {
        return date('YmdHis') . "_{$userId}_" . mt_rand(10000000, 99999999);
    }

    /**
     * 生成商家订单号
     */
    static function genOrderId($userId = 0)
    {
        return date('YmdHis') . "_{$userId}_" . mt_rand(10000000, 99999999);
    }

    /**
     * 根据out_trade_no查询订单记录
     */
    static function getByOutTradeNo($outTradeNo = '')
    {
        if (!$outTradeNo) return null;
        $item = self::where('out_trade_no', $outTradeNo)->first();
        return $item ? $item->toArray() : null;
    }

    /**
     * 根据wx_prepay_id查询订单记录
     */
    static function getByWxPrepayId($wxPrepayId = '')
    {
        if (!$wxPrepayId) return null;
        $item = self::where('wx_prepay_id', $wxPrepayId)->first();
        return $item ? $item->toArray() : null;
    }

    /**
     * 根据内部订单号查询订单信息
     */
    static function getByOrderId($userId = 0, $orderId = '')
    {
        $where = ['user_id' => $userId, 'order_id' => $orderId];
        $item = self::where($where)->first();
        return $item ? $item->toArray() : null;
    }

    /**
     * 查询充值成功的用户
     */
    static function getRechargeUsers($gender = -1, $profileWhere = [])
    {
        $where = [
            ['status', self::STATUS_OK]
        ];
        if ($gender > -1) $where[] = ['user_profile.gender', '=', $gender];
        if ($profileWhere) $where = array_merge($where, $profileWhere);
        $userIds = Db::table('pay_order')->join('user_profile', function ($join) {
            $join->on('pay_order.user_id', '=', 'user_profile.user_id');
        })->where($where)->orderBy('user_profile.online', 'desc')->orderBy('user_profile.updated_at', 'desc')->pluck('pay_order.user_id')->toArray();
        return array_values(array_unique($userIds));
    }

    /*****************统计相关***************/
    /**
     * 充值总人数
     */
    static function getRechargeCount($userIds = [], $startTime = '', $endTime = '', $channel = '')
    {
        $where = [
            ['pay_order.status', self::STATUS_OK],
        ];
        if ($startTime) $where[] = ['finished_at', '>=', (string)$startTime];
        if ($endTime) $where[] = ['finished_at', '<', (string)$endTime];
        //指定渠道
        if (!$channel) {
            $query = self::where($where);
        } else {
            $query = Db::table('pay_order')->join('user', function ($join) use ($where, $channel) {
                $where[] = ['user.channel', $channel];
                $join->on('user.id', '=', 'pay_order.user_id')->where($where);
            });
        }
        //指定用户
        if ($userIds) {
            $data = $query->whereIn('user_id', $userIds)->select(Db::raw('count(distinct user_id) as ctn'))->first();
        } else {
            $data = $query->select(Db::raw('count(distinct user_id) as ctn'))->first();
        }
        // dump($where);
        return $data && $data->ctn ? $data->ctn : 0;
    }

    /**
     * 充值总金额
     */
    static function getRechargeCoin($userIds = [], $startTime = '', $endTime = '', $channel = '', $type = 'coin')
    {
        $where = [
            ['pay_order.status', self::STATUS_OK],
        ];
        if ($startTime) $where[] = ['finished_at', '>=', (string)$startTime];
        if ($endTime) $where[] = ['finished_at', '<', (string)$endTime];
        //指定渠道
        if (!$channel) {
            $query = self::where($where);
        } else {
            $query = Db::table('pay_order')->join('user', function ($join) use ($where, $channel) {
                $where[] = ['user.channel', $channel];
                $join->on('user.id', '=', 'pay_order.user_id')->where($where);
            });
        }
        //指定用户
        switch ($type) {
            case 'coin':
                $field = 'recharge_coin';
                break;
            case 'send_coin':
                $field = 'send_coin';
                break;
            default:
                $field = 'total_amount';
                break;
        }
        if ($userIds) {
            $data = $query->whereIn('user_id', $userIds)->select(Db::raw('sum(' . $field . ') as total'))->first();
        } else {
            $data = $query->select(Db::raw('sum(' . $field . ') as total'))->first();
        }
        return $data && $data->total ? $data->total : 0;
    }


    /**
     * 实时充值日志
     */
    static function getRechargeNum($type = '', $start = '', $end = '')
    {
        $failKeys = ['fail_num', 'fail_user_num'];
        //待支付
        if (in_array($type, $failKeys)) {
            $where = [['status', self::STATUS_ING]];
            if ($start) $where[] = ['created_at', '>=', (string)$start];
            if ($end) $where[] = ['created_at', '<', (string)$end];
        } else {
            $where = [['status', self::STATUS_OK]];
            if ($start) $where[] = ['finished_at', '>=', (string)$start];
            if ($end) $where[] = ['finished_at', '<', (string)$end];
        }
        switch ($type) {
            case 'pay_order_num':
                return self::where($where)->count();
            case 'pay_user_num':
                $data = self::where($where)->select(Db::raw('count(distinct user_id) as ctn'))->first();
                return $data && $data->ctn ? $data->ctn : 0;
            case 'pay_rmb_num':
                $data = self::where($where)->select(Db::raw('sum(total_amount) as rmb'))->first();
                return $data && $data->rmb ? $data->rmb : 0;
            case 'wechat_order_num':
                $channels = [self::CHANNEL_SAND_WX, self::CHANNEL_WX];
                return self::where($where)->whereIn('channel', $channels)->count();
            case 'wechat_rmb_num':
                $channels = [self::CHANNEL_SAND_WX, self::CHANNEL_WX];
                $data = self::where($where)->whereIn('channel', $channels)->select(Db::raw('sum(total_amount) as rmb'))->first();
                return $data && $data->rmb ? $data->rmb : 0;
            case 'alipay_order_num':
                $channels = [self::CHANNEL_SAND_ALI, self::CHANNEL_ALI];
                return self::where($where)->whereIn('channel', $channels)->count();
            case 'alipay_rmb_num':
                $channels = [self::CHANNEL_SAND_ALI, self::CHANNEL_ALI];
                $data = self::where($where)->whereIn('channel', $channels)->select(Db::raw('sum(total_amount) as rmb'))->first();
                return $data && $data->rmb ? $data->rmb : 0;
            case 'fail_num':
                return self::where($where)->count();
            case 'fail_user_num':
                $data = self::where($where)->select(Db::raw('count(distinct user_id) as ctn'))->first();
                return $data && $data->ctn ? $data->ctn : 0;
        }
        return 0;
    }

    /**
     * 邀请充值金额
     */
    static function getInviteData($start = '', $end = '', $invited = true, $type = 'rmb')
    {
        $where = [
            ['finished_at', '>=', $start],
            ['finished_at', '<', $end],
            ['pay_order.status', self::STATUS_OK]
        ];
        if ($invited) $where[] = ['invite_code', '<>', ''];
        else $where[] = ['invite_code', '=', ''];
        $query = Db::table('pay_order')->leftJoin('user_profile', function ($join) {
            $join->on('user_profile.user_id', '=', 'pay_order.user_id');
        })->where($where);
        if ($type == 'rmb') {
            $data = $query->select(Db::raw('sum(total_amount) as rmb'))->first();
            return $data && $data->rmb ? $data->rmb : 0;
        } elseif ($type == 'user') {
            $data = $query->select(Db::raw('count(distinct t_pay_order.user_id) as ctn'))->first();
            return $data && $data->ctn ? $data->ctn : 0;
        }
        return 0;
    }

    /**
     * 新注册用户充值的
     */
    static function getNewRechargeUsers($startTime = '', $endTime = '')
    {
        $where = [
            ['pay_order.status', self::STATUS_OK],
        ];
        if ($startTime) {
            $where[] = ['user_profile.created_at', '>=', (string)$startTime];
            $where[] = ['finished_at', '>=', (string)$startTime];
        }
        if ($endTime) {
            $where[] = ['user_profile.created_at', '<', (string)$endTime];
            $where[] = ['finished_at', '<', (string)$endTime];
        }
        //指定渠道
        $userIds = Db::table('pay_order')->join('user_profile', function ($join) {
            $join->on('user_profile.user_id', '=', 'pay_order.user_id');
        })->where($where)->pluck('user_profile.user_id')->toArray();
        return $userIds ? array_unique($userIds) : [];
    }

    /**
     * 获取当日充值金币
     */
    static function checkSuccessAmountCoin($userId = 0, $startTime = '')
    {
        $where = [
            ['user_id', $userId],
            ['status', self::STATUS_OK]
        ];
        if ($startTime) $where[] = ['finished_at', '>', (string)$startTime];
        $coin = self::where($where)->select(Db::raw('SUM(recharge_coin) as recharge_coin,SUM(send_coin) as send_coin,SUM(total_amount) as amount'))->first()->toArray();
        return $coin;
    }


    /**
     * 查询充值成功的用户
     * @Cacheable(prefix="get_recharge_users_on_days", ttl=30,value="_#{day}", listener="get_recharge_users_on_days")
     */
    static function getRechargeUsersOnDays($day=7)
    {
        $where = [
            ['status', self::STATUS_OK]
        ];
        $userIds = Db::table('pay_order')
            ->where($where)
            ->orderBy('id',desc)
            ->limit(500)
            ->pluck('user_id')
            ->toArray();
        return array_values(array_unique($userIds));
    }

    /**
     * 用户首次支付成功的记录
     */
    static function checkSuccessCounts($userId = 0)
    {
        $where = [
            ['user_id', $userId],
        ];
        return self::where($where)->whereIn('status', [self::STATUS_OK])->count() ?? 0;
    }

    /**
     *
     * @param $userId
     * @return Builder|\Hyperf\Database\Model\Model|object|bool
     */
    static function getUserIsPay($userId){
        return self::where([
            ['user_id','=',$userId],
            ['status','=',self::STATUS_OK]
        ])->first() ?? false;
    }

    /**
     * @Cacheable(prefix="pay_list", ttl=30, listener="pay_list")
     * 获取所有已经充值的人
     * @return array
     */
    public static function getIsPayAll()
    {
        return self::where([
            ['status','=',self::STATUS_OK]
        ])->pluck('user_id')->toArray();
    }
}
